package com.lofter.heras.hash;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.DigestException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * file hash
 * 
 * @author heras
 * 
 */
public class Hash {

	public static Hash getInstance() {
		synchronized (Hash.class) {
			if (instance == null) {
				instance = new Hash();
			}
		}
		return instance;
	}

	private Hash() {
	}

	private Handler handler;

	/**
	 * hash handler
	 * @return hash handler
	 */
	public Handler getHandler() {
		return handler;
	}

	public void setHandler(Handler handler) {
		this.handler = handler;
	}

	/**
	 * file hash
	 * 
	 * @param file
	 *            path of file
	 * @param type
	 *            type of hash,"SHA1" or "MD5"
	 * @return hash code of the file
	 * @throws NoSuchAlgorithmException
	 * @throws DigestException
	 * @throws IOException
	 */
	public String hashFile(String file, String type)
			throws NoSuchAlgorithmException, DigestException, IOException {
		String result = "";
		if ("md5".equalsIgnoreCase(type) || "sha1".equalsIgnoreCase(type)) {

			MessageDigest hash = MessageDigest.getInstance(type);
			File f = new File(file);
			FileInputStream fs = new FileInputStream(f);
			long total = f.length();
			long saved = 0;
			byte[] bytes = new byte[BUFFER_SIZE];
			int len = -1;
			while ((len = fs.read(bytes)) != -1) {
				saved += len;
				hash.update(bytes, 0, len);
				if (handler != null) {
					handler.update(saved, total);
				}
			}
			fs.close();
			fs=null;
			if (handler != null) {
				handler.complete();
			}
			result = toHexString(hash.digest());
			return result.toUpperCase();
		}
		throw new IOException("invalid hash type");
	}

	/**
	 * hex string
	 * 
	 * @param bytes
	 * @return
	 */
	private String toHexString(byte[] bytes) {
		if (bytes != null) {
			StringBuilder builder = new StringBuilder();
			for (int i = 0; i < bytes.length; i++) {
				builder.append(HashChars[(bytes[i] & 0xf0) >>> 4]);
				builder.append(HashChars[bytes[i] & 0x0f]);
			}
			return builder.toString();
		}
		return "";
	}

	public static final int BUFFER_SIZE = 10240;
	public final char[] HashChars = { '0', '1', '2', '3', '4', '5', '6', '7',
			'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
	private static Hash instance;

}
